{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "vscode": {
     "languageId": "plaintext"
    }
   },
   "source": [
    "# Tutorial 5d - User-defined Optimization Operands"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "tags": [
     "nbsphinx-toctree"
    ]
   },
   "source": [
    "### November 2024"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This tutorial demonstrates how to create a user-defined optimization metric in Optiland. This allows for the optimization of essentially any custom metric, providing flexibility to tailor the optimization process to specific needs. By the end of this tutorial, you will be able to:\n",
    "\n",
    "- Define custom optimization metrics.\n",
    "- Integrate these metrics into the Optiland optimization framework.\n",
    "- Apply the custom metrics to optimize an optical system.\n",
    "\n",
    "In this example, we will create a custom metric to optimize a freeform surface such that the spot forms an ellipse on the image plane. In particular, we will enforce that the ratio of the ellipse axes equals the golden ratio, or ≈1.618."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "All operands, or optimization metrics, in Optiland are defined in the so-called _operand registry_. By defining, then adding a new metric into this registry, we can use it like any other optimization metric. More details of the operand registry can be found in the documentation or in `optiland.optimization.operand`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "from optiland import analysis, optic, optimization\n",
    "from optiland.optimization.operand import operand_registry"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We start by defining the function that computes our custom metric. The only requirement is that the function returns a scalar value. We can perform any analyses we want within the function and we can choose to include any input arguments we wish.\n",
    "\n",
    "We will define the function `spot_ellipse_ratio`, which traces rays through the provided lens system, then computes the ratio of the major and minor axes of the spot diagram (ray intersection points at the image plane)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "def spot_ellipse_ratio(lens):\n",
    "    \"\"\"Find the ratio of the major and minor axes for an elliptical spot diagram.\n",
    "\n",
    "    This function traces rays through the lens and computes the ratio of the major and\n",
    "    minor axes of the ellipse that best fits the spot diagram.\n",
    "\n",
    "    Args:\n",
    "        lens: The lens object.\n",
    "\n",
    "    Returns:\n",
    "        The ratio of the major and minor axes of the ellipse.\n",
    "\n",
    "    \"\"\"\n",
    "    rays_out = lens.trace(\n",
    "        Hx=0,\n",
    "        Hy=0,\n",
    "        wavelength=0.55,\n",
    "        num_rays=15,\n",
    "        distribution=\"hexapolar\",\n",
    "    )\n",
    "\n",
    "    # Extract the x and y coordinates of the rays at last surface = image plane\n",
    "    x = rays_out.x\n",
    "    y = rays_out.y\n",
    "\n",
    "    # Stack x and y to compute the covariance matrix of the points\n",
    "    points = np.vstack((x, y))\n",
    "    cov_matrix = np.cov(points)\n",
    "\n",
    "    # Calculate the eigenvalues of the covariance matrix\n",
    "    eigenvalues = np.linalg.eigvalsh(cov_matrix)\n",
    "\n",
    "    # Sort eigenvalues to identify the major and minor axes (a >= b)\n",
    "    a2, b2 = np.sort(eigenvalues)[::-1]\n",
    "\n",
    "    # Calculate ratio of major and minor axes\n",
    "    a = np.sqrt(a2)\n",
    "    b = np.sqrt(b2)\n",
    "    ratio = a / b\n",
    "\n",
    "    return ratio"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We now simply add our function to the operand registry, which was imported from `optiland.optimization.operand`. We need to provide the name of the operand and the function itself. We choose the name `'ellipse_ratio'`, which we will use to reference our function later on. Lastly, we may choose to allow overwriting an existing registered function by passing `overwrite=True`. This is useful if you are testing the function and making frequent changes. Without allowing overwriting, an error will be raised."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "operand_registry.register(\"ellipse_ratio\", spot_ellipse_ratio, overwrite=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we define a simple singlet value with a polynomial surface type on the first surface. Other tutorials (see [here](https://github.com/HarrisonKramer/optiland/blob/master/docs/examples/Tutorial_7c_Freeform_Surfaces.ipynb)) and the documentation elaborate on polynomial surfaces. We will omit those details here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Singlet(optic.Optic):\n",
    "    def __init__(self):\n",
    "        super().__init__()\n",
    "\n",
    "        # add surfaces\n",
    "        self.add_surface(index=0, radius=np.inf, thickness=np.inf)\n",
    "\n",
    "        self.add_surface(\n",
    "            index=1,\n",
    "            radius=100,\n",
    "            thickness=5,\n",
    "            surface_type=\"polynomial\",\n",
    "            is_stop=True,\n",
    "            material=\"SF11\",\n",
    "            coefficients=[],\n",
    "        )\n",
    "\n",
    "        self.add_surface(index=2, thickness=100)\n",
    "        self.add_surface(index=3)\n",
    "\n",
    "        # add aperture\n",
    "        self.set_aperture(aperture_type=\"EPD\", value=25)\n",
    "\n",
    "        # add field\n",
    "        self.set_field_type(field_type=\"angle\")\n",
    "        self.add_field(y=0)\n",
    "\n",
    "        # add wavelength\n",
    "        self.add_wavelength(value=0.55, is_primary=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We create a view the initial lens."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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oM8mX3d395DXFPrGoP7dUvRXfsumf7TVN57WrVuXfIz+/dM+1rVXRSXXlTKQW5988kl8v/32i3/pS18yl7dv35487JVeGhsb42effXb87bffjj/yyCPx8vLy+IoVK3J67sVKv/9aeeehhx4yVXcuvPDCeE1NTXzHjh25PrWid8kll8Srq6vjL7zwQspzqbu7O/mYiy++OD5r1qz4c889F1+7dm180aJF5kB+sFf/U/RXflmzZk3c4/HEb7vttvh7770Xf/jhh83fo1/84hfJx9xxxx3md+J///d/x9988834KaecYira9vT05PTci9HSpUvj++yzT/yJJ56Ib9q0Kf7oo4/G6+rq4t/97neTj6G/cl/99LXXXjOHxoIf/ehH5vLmzZuz7p8TTzwxfsQRR8RfeeWV+EsvvWSqqf7DP/xDvNgQqorcgw8+aJ5EmQ67N954w5Sx1Rfz+gtSn2TInXvvvde80PP5fKbE+ssvv5zrU0KiHG2mQ59nFv1DdOmll5ptCvTF4Fe/+tWUNzGQX6GK/so/v//97+OHHHKI+Xt04IEHxn/2s5+l3K9loK+//nrzZqA+Rt80fPfdd3N2vsWso6PDPJ/071VpaWl87ty58X/6p39K2aKF/sqt559/PuPfLQ3E2fbPrl27TIiqrKyMV1VVxc877zwT1oqNS//J9WgZAAAAADgVa6oAAAAAYAwIVQAAAAAwBoQqAAAAABgDQhUAAAAAjAGhCgAAAADGgFAFAAAAAGNAqAIAAACAMSBUAQCKxuzZs8Xlcpmjra1t0j//Cy+8kPz8p5566qR/fgDAxCBUAQAcxR5MMh3HH3/8iB9/yy23yPbt26W6ulom22c+8xnzuf/+7/9+0j83AGDieCbw/wYAYMKCSbrHH39cLr74Yrn00ktH/PhAICBTp06VXPD5fOZzl5WVSTgczsk5AADGHyNVAABHsYKJ/WhtbZXvfOc78r3vfU9OP/30Uf1/Dz30kNTU1MgTTzwh8+fPl/Lycvm7v/s76e7ulpUrV5opg8FgUK644grp7+9Pfpze/v3vf1/OOeccqayslH333dcEu507d8opp5xibjv00ENl7dq1E/BdAADkE0IVAMDRdG2UhpjjjjtObr311r36PzRA3XPPPfLII4/I008/baYYfvWrX5U//OEP5vjP//xPWbFihfz2t79N+bgf//jHcuyxx8prr70mS5YskbPPPtuErLPOOkvWr18v8+bNM9fj8fg4fbUAgHzE9D8AgGPFYjE588wzxePxyMMPP2zWVO2NaDQq999/vwlBSkeqNEg1NTWZEacFCxaYtVrPP/+8fP3rX09+3N/+7d/KRRddZC7fcMMN5v846qijkqNlV199tSxatMj8P7macggAmHiEKgCAY+l0v9WrV8uaNWvMWqm9pVP+rEClGhsbzfQ+DVT225qbm1M+Tqf32e9XCxcuHHKbfhyhCgAKF6EKAOBIOlXvhz/8oTz55JOy//77j+n/8nq9Kdd1xCvTbToyNtzHWaNkmW5L/zgAQGFhTRUAwHFef/11ueCCC+SOO+6QxYsX5/p0AABFjpEqAICjtLS0mI1ztTCFFoTYsWNHyv0lJSVSX1+fs/MDABQfQhUAwFF0ut/mzZvNMW3atCH3a2nzjz76KCfnBgAoTq44dV4BAEVCi09ceeWV5silc88915SCf+yxx3J6HgCA8cGaKgBAUdEy51rVr729fdI/9//93/+Zz63l3wEAhYORKgBA0dApg7onlZo7d6643ZP73mJPT49s3brVXNZwRZl1ACgMhCoAAAAAGAOm/wEAAADAGBCqAAAAAGAMCFUAAAAAMAaEKgAAAAAYA0IVAAAAAIwBoQoAAAAAxoBQBQAAAABjQKgCAAAAgDEgVAEAAACA7L3/Dyq6U27tkfL3AAAAAElFTkSuQmCC",
      "text/plain": [
       "<Figure size 1000x400 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "lens = Singlet()\n",
    "lens.draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We now define the optimization problem. We start by defining an OptimizationProblem instance."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "problem = optimization.OptimizationProblem()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we add our custom optimization operand to the optimization problem. Recall that we called our new metric `'ellipse_ratio'`. We will specify this as the operand type and set the target to 1.618. Note that we must also provide an `input_data` argument. This is a dictionary in which the keys must match the arguments of our function. As our function only requires the argument `lens`, this is the only input we include. A user-defined function can have as many arguments as you wish, but all must be included in the `input_data` dictionary with their values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "input_data = {\"lens\": lens}\n",
    "problem.add_operand(\n",
    "    operand_type=\"ellipse_ratio\",\n",
    "    target=1.618,\n",
    "    weight=1,\n",
    "    input_data=input_data,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We now add variables representing the coefficients of our polynomial surface."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "for i in range(2):\n",
    "    for j in range(2):\n",
    "        problem.add_variable(\n",
    "            lens,\n",
    "            \"polynomial_coeff\",\n",
    "            surface_number=1,\n",
    "            coeff_index=(i, j),\n",
    "        )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's view the details of the optimization problem to see its current, non-optimized status"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "╒════╤════════════════════════╤═══════════════════╕\n",
      "│    │   Merit Function Value │   Improvement (%) │\n",
      "╞════╪════════════════════════╪═══════════════════╡\n",
      "│  0 │               0.381924 │                 0 │\n",
      "╘════╧════════════════════════╧═══════════════════╛\n",
      "╒════╤════════════════╤══════════╤══════════════╤══════════════╤══════════╤═════════╤═════════╤════════════════╕\n",
      "│    │ Operand Type   │   Target │ Min. Bound   │ Max. Bound   │   Weight │   Value │   Delta │   Contrib. [%] │\n",
      "╞════╪════════════════╪══════════╪══════════════╪══════════════╪══════════╪═════════╪═════════╪════════════════╡\n",
      "│  0 │ ellipse ratio  │    1.618 │              │              │        1 │       1 │  -0.618 │            100 │\n",
      "╘════╧════════════════╧══════════╧══════════════╧══════════════╧══════════╧═════════╧═════════╧════════════════╛\n",
      "╒════╤══════════════════╤═══════════╤═════════╤══════════════╤══════════════╕\n",
      "│    │ Variable Type    │   Surface │   Value │ Min. Bound   │ Max. Bound   │\n",
      "╞════╪══════════════════╪═══════════╪═════════╪══════════════╪══════════════╡\n",
      "│  0 │ polynomial_coeff │         1 │       0 │              │              │\n",
      "│  1 │ polynomial_coeff │         1 │       0 │              │              │\n",
      "│  2 │ polynomial_coeff │         1 │       0 │              │              │\n",
      "│  3 │ polynomial_coeff │         1 │       0 │              │              │\n",
      "╘════╧══════════════════╧═══════════╧═════════╧══════════════╧══════════════╛\n"
     ]
    }
   ],
   "source": [
    "problem.info()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, we create our optimizer and optimize the system."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "optimizer = optimization.OptimizerGeneric(problem)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "res = optimizer.optimize(tol=1e-6)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can optionally view the optimization output details. We see that the optimization was successful and that the function was called 140 times. On the author's computer, the optimization completed in 0.3 seconds."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "  message: CONVERGENCE: RELATIVE REDUCTION OF F <= FACTR*EPSMCH\n",
       "  success: True\n",
       "   status: 0\n",
       "      fun: 4.610224594082534e-12\n",
       "        x: [ 3.312e-05 -2.164e-10 -4.314e-10  9.576e-03]\n",
       "      nit: 5\n",
       "      jac: [ 1.026e-07  9.535e-14  0.000e+00 -2.927e-04]\n",
       "     nfev: 140\n",
       "     njev: 28\n",
       " hess_inv: <4x4 LbfgsInvHessProduct with dtype=float64>"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "res"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We now view the optimization problem result. The optimization was indeed a success and we see that our operand has the target value of ≈1.618."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "╒════╤════════════════════════╤═══════════════════╕\n",
      "│    │   Merit Function Value │   Improvement (%) │\n",
      "╞════╪════════════════════════╪═══════════════════╡\n",
      "│  0 │            4.61022e-12 │               100 │\n",
      "╘════╧════════════════════════╧═══════════════════╛\n",
      "╒════╤════════════════╤══════════╤══════════════╤══════════════╤══════════╤═════════╤═════════╤════════════════╕\n",
      "│    │ Operand Type   │   Target │ Min. Bound   │ Max. Bound   │   Weight │   Value │   Delta │   Contrib. [%] │\n",
      "╞════╪════════════════╪══════════╪══════════════╪══════════════╪══════════╪═════════╪═════════╪════════════════╡\n",
      "│  0 │ ellipse ratio  │    1.618 │              │              │        1 │   1.618 │       0 │            100 │\n",
      "╘════╧════════════════╧══════════╧══════════════╧══════════════╧══════════╧═════════╧═════════╧════════════════╛\n",
      "╒════╤══════════════════╤═══════════╤══════════════╤══════════════╤══════════════╕\n",
      "│    │ Variable Type    │   Surface │        Value │ Min. Bound   │ Max. Bound   │\n",
      "╞════╪══════════════════╪═══════════╪══════════════╪══════════════╪══════════════╡\n",
      "│  0 │ polynomial_coeff │         1 │  3.31221e-05 │              │              │\n",
      "│  1 │ polynomial_coeff │         1 │ -2.16406e-10 │              │              │\n",
      "│  2 │ polynomial_coeff │         1 │ -4.31446e-10 │              │              │\n",
      "│  3 │ polynomial_coeff │         1 │  0.00957621  │              │              │\n",
      "╘════╧══════════════════╧═══════════╧══════════════╧══════════════╧══════════════╛\n"
     ]
    }
   ],
   "source": [
    "problem.info()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Lastly, let's view the spot diagram to see if it truly appears elliptical."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAeYAAAFdCAYAAADWns55AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjEsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvc2/+5QAAAAlwSFlzAAAPYQAAD2EBqD+naQAAZxBJREFUeJztnQd4HPWd/r+rXqxqq9iWZVvYGGyMZYqNSQCTGJtyXKiBAMbkOCCEcrQE+IceAgFCCRwXEi6hhaNcCgkJIUACKYcx1SRgMLg3yVXdVrP2/7w/M2I0mr6zu7O77+d51vLOTvnNrDTvfH/fFolGo1EhhBBCSCjISvYACCGEEPI5FGZCCCEkRFCYCSGEkBBBYSaEEEJCBIWZEEIICREUZkIIISREUJgJIYSQEEFhJoQQQkIEhZkQQggJERRmQgghJERQmNOMRx99VCKRiLz99tumn8+dO1f222+/hIzlt7/9rRxwwAFSUFAg9fX1cuONN0p/f7+rbQcGBuTOO++UiRMnqu33339/eeqpp0zX/eijj+Too4+WESNGSGVlpSxcuFC2bt0a0z7dsGbNGnWtf/CDH5h+ftNNN6nPt23bJvEk3a8zIZkGhZnEhT/84Q9ywgknSHl5uTzwwAPq/7feeqtccsklrrb/zne+I1dffbUcddRRansIzhlnnCFPP/30kPU2bNgghx9+uKxYsUJuu+02ueqqq+T3v/+92q63t9fXPlMJXmdC0hA0sSDpwyOPPIKmJNG33nrL9PMjjjgiOm3atLiPY+rUqdEZM2ZE+/r6Bpd95zvfiUYikehHH31ku+2GDRuiubm50Ysuumhw2cDAQPSwww6L1tXVRfv7+weXX3jhhdHCwsLo2rVrB5e9/PLL6hr8+Mc/9rVPt6xevVod56677jL9/MYbb1Sfb926NRovMuE6E5Jp0GLOcB555BE13fqzn/1syHJYRVj+wgsvDC5ramqSjz/+WPr6+mz3uWzZMvU6//zzJScnZ3D5N7/5TTwIyi9+8Qvb7X/zm9+oY2B9DYzlwgsvVJbb4sWLB5f/8pe/lH/5l39RVpnGvHnzZO+995Znn33W1z7jBaaYc3NzTad/ca1g9XZ3d/M6E5LhUJjTlLa2NuXbNL6MN/uvf/3r6oZ7xRVXyPr169Wyf/7zn3LzzTfLueeeK8cee+zgutdee63su+++snHjRttjv/fee+rnQQcdNGT5mDFjpK6ubvBzu+2Li4vVsfTMmjVryP4xji1btgw7jrau/jhu9+mHnTt3ml5rLNcDnyx8v88888yQ5ZgKhoiefPLJyifL60xIZkNhTlNgzVRVVQ17vf7668PWffjhhyU7O1sJMURi0aJFUltbK/fcc4+vY8PiA6NHjx72GZZt2rTJcfuamhplaRm3Bdr2TsfZsWOH9PT0eNqnX0vY7FrfddddQ9abNGmSzJkzR37+858PWQ5fbUtLixJuL2TadSYkU/h8/oukFQ8++KCaZjRy5ZVXyu7du4csgwhj/a997Wty2GGHydKlS+Xll1+W0tLSYRHfeDmxa9cu9TM/P3/YZ7AI29vbHbe32la/f6fj6Pfldp9+wFTyqaeeOmz5448/Lk888cSQZWeffbaa1l25cqXstddeatmTTz4p48aNkyOOOEK953UmJLOhMKcpmDo0m3qsqKgwTd85/fTTlSUH6w1C8+Uvf9n3sQsLC9VPzYrSAx+q9rnd9lbb6vfvdBzjum7W88PkyZPVDIWRv//978OWnXbaaXLZZZcpMb7hhhuUy+F3v/udXH755cOsTCcy7ToTkilwKpsotm/fPpj7jIAi5KL6RZu21KZA9WAZfKBO2zc3N6sAJuO2QNve6TjItdWsN7f7jDd4MIJPH8IM4FuGkJ111lme98XrTEh6QmEmiosuukg6Ojrk9ttvV5befffd53tfjY2N6qexyAn8i4jM1T632x6BUyhooWfJkiVD9j927FjlyzUrpvLmm28OOY7bfSYCTGd/8skn8tZbbymBnjlzpkybNs3zfnidCUlTkp2vRZKfx/y///u/apv7779fvT/99NNVzury5cuHrLdp0yaVG9vb2+s4jn322Ufl1+rzVq+77jqVX7ts2bLBZa2trWqf+Kmxfv16y1zYsWPHDtnnN77xDTXWdevWDS575ZVX1Pn86Ec/8rXPeOcx4/qNGjUqevLJJ0ezsrKid99995DPeZ0JyWwozBkuzJs3b1YiceSRR6obKNi2bVu0pqYmOmfOnOju3bsH1120aJHaNwTJieeff16Jw5e+9KXoT37yk+ill16qROi8884zHS9+6vnWt76llp9//vnRhx9+OHrcccep908++eSQ9SAUI0eOjO61117qweK2226LVlRURKdPnx7t7u72tU+rMQVZYOTi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QggJERRmQgghJERQmAkhhJAQQWEmhBBCQgSFOQl873vfk0MPPVSKioqkvLzcdJ1169bJcccdp9aprq6Wb33rW9Lf3y+pwoQJE1TlNP3r+9//vqQSDz74oDqPgoICmT17trz55puSitx0003Dvot99tlHUom//vWvcvzxx6uqWxj/c889N+RzxLDecMMNMnr0aCksLJR58+bJp59+mrTxEhILFOYk0NvbK6eeeqpceOGFpp/v3r1biTLWe/311+Wxxx6TRx99VN14UolbbrlFmpqaBl+XXHKJpArPPPOMXHHFFSoV591335UZM2bIggULZMuWLZKKTJs2bch38fe//11Sia6uLvUd4GHJjDvvvFPuv/9+eeihh2TJkiVSXFysvq/u7u6Ej5WQmEG6FEkOjzzySLSsrGzY8hdeeCGalZUVbW5uHlz2ox/9KFpaWhrt6emJpgLjx4+P3nvvvdFUZdasWdGLLrpo8P3u3bujY8aMid5+++3RVOPGG2+MzpgxI5ou4Lb161//evD9wMBAtLa2NnrXXXcNLmttbY3m5+dHn3rqqSSNkhD/0GIOIYsXL5bp06dLTU3N4DI8/aNg/4cffiipAqauR44cKTNnzpS77rorZabiMVPxzjvvqOlQfQ11vMd3k4pgWhfTwA0NDXLmmWcqV0m6sHr1amlubh7yfaGmNdwPqfp9kcyGTSxCCG4yelEG2nt8lgpceumlcsABB0hlZaWajr/22mvVFOo999wjYWfbtm3KnWD2HXz88ceSakCg4AqZMmWK+g5uvvlmOeyww+SDDz5QXdRSHe1vwuz7SpW/F0L00GIOiGuuuWZYgI3xlYo3db/nCP/s3LlzZf/995dvfOMbcvfdd8sDDzwgPT09yT6NjOOYY45RMQ34LjDz8sILL0hra6s8++yzyR4aIcQEWswBceWVV8o555xjuw6mEd1QW1s7LAJ48+bNg5+l4jnCasNU9po1a5TlFmZGjRol2dnZg9dcA++Tef2DApkAe++9t6xYsULSAe07wfeDqGwNvG9sbEziyAjxB4U5IKqqqtQrCObMmaNSqhABjFQp8PLLL6t+s1OnTpVUPMelS5cqP612PmEmLy9PDjzwQPnTn/4kJ5xwglo2MDCg3l988cWS6nR2dsrKlStl4cKFkg5MnDhRiTO+H02IEY+B6GyrzAdCwgyFOQkg8GbHjh3qJ3yZEC0wadIkGTFihMyfP18JMG6cSAOBn+y6666Tiy66KCW66CDgBjfFI488Uvkw8f7yyy+Xs846SyoqKiQVwFT8okWL5KCDDpJZs2bJfffdp1J2vv71r0uqcdVVV6kc4PHjx8umTZtUChhmBL72ta9JKj1M6C18BHzh7wYxDPX19XLZZZfJrbfeKpMnT1ZCff3116tgN+3BipCUIoaIbuKTRYsWqZQP4+vVV18dXGfNmjXRY445JlpYWBgdNWpU9Morr4z29fVFU4F33nknOnv2bJUKVlBQEN13332jt912W7S7uzuaSjzwwAPR+vr6aF5enkqfeuONN6KpyGmnnRYdPXq0Oo+xY8eq9ytWrIimEvjbMPubwd+SljJ1/fXXR2tqalSa1Je//OXo8uXLkz1sQnzBto+EEEJIiGBUNiGEEBIiKMyEEEJIiKAwE0IIISGCwkwIIYSECAozIYQQEiIozIQQQkiIoDATQgghIYLCTAghhIQICjPJSJYvX67qK3d0dCT82C+++KKq6Yz624QQYoTCTFIS1Bg/9NBD5aSTThqyvK2tTcaNGyff+c53bLdHf+hLLrkkKf2Ijz76aMnNzZUnn3wy4ccmhIQfluQkKcsnn3yiLM+HH35YzjzzTLXs7LPPlvfff1/eeust1SXKDDQPQcMQNEIYO3asJIMHH3xQHn30UTVOQgjRQ4uZpCzoKfz9739fWb5NTU3ym9/8Rp5++ml5/PHHLUUZPPvsszJjxowhogyRRJ/i3/3ud6pfdFFRkZxyyimyc+dOeeyxx2TChAmqM9all16qrHUNLEdXIzwQoDMYOjj99re/la1bt8pXvvIVtWz//feXt99+e8gY0O0Jy9B+kRBC9FCYSUoDUYbIokXm+eefLzfccIN6b8ff/vY31c7RCET4/vvvV+IOP/Brr70mJ554orzwwgvq9cQTT8iPf/xj+cUvfjFku3vvvVe+8IUvyHvvvSfHHXecGguEGm0u3333Xdlrr73Ue/3kFFoV1tTUqLEQQoge9mMmKU0kEpEf/ehHsu+++8r06dPlmmuucdxm7dq1psLc19en9gUhBbCYIcabN29Wli96ZKPH9KuvviqnnXba4HbHHnusXHDBBer/eDDAPg4++GA59dRT1bKrr75a5syZo/aDgDMN9AvGWAghRA8tZpLy/OxnP1NTz/AZb9iwwXH9Xbt2SUFBwbDl2IcmygAWLaaqIcr6ZVu2bBmyHaaq9Z8DPCQYlxm3KywsVFY6IYTooTCTlOb1119XU8nwDc+aNUvOPffcIVPGZowaNUpaWlqGLUektNEaN1tmTHPSr4PPrZYZt9uxY4dUVVW5OEtCSCZBYSYpC6zNc845Ry688EI1xfzTn/5U3nzzTXnooYdst5s5c6YsW7ZMkkl3d7cK/MJYCCFED4WZpCzIRYZ1jMhsgGnnH/zgB/Ltb39b1qxZY7ndggULZPHixUOiqxPNG2+8Ifn5+cr3TAgheijMJCX5y1/+onKBH3nkEeUb1kAQFgqP2E1pH3PMMZKTkyOvvPKKJIunnnpK5V7rx04IIYAFRkhGAlFHvvEf//jHhB9727ZtKlcaecwTJ05M+PEJIeGG6VIkI4Fl3draqmplJ7osJ6bZ/+u//ouiTAgxhRYzIYQQEiLoYyaEEEJCBIWZEEIICREUZkIIISREUJgJIYSQEEFhJoQQQkIEhZkQQggJERRmQgghJERQmAkhhJAQQWEmhBBCJDz8fzL+tN3deCgxAAAAAElFTkSuQmCC",
      "text/plain": [
       "<Figure size 1200x400 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "spot = analysis.SpotDiagram(lens, num_rings=15)\n",
    "spot.view()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Conclusions:\n",
    "\n",
    "- This tutorial showed how to define a custom optimization operand in Optiland and how to use it within an optimization problem.\n",
    "- The operand registry defines all optimization operands in Optiland. Adding a user-defined operand to this registry makes it available for optimization.\n",
    "- An operand function requires only that a scalar output value is generated. The function may have any number of input arguments. The values of the input arguments must be passed as input data to the operand when it is defined for the optimization problem.\n",
    "- Following the steps here, any arbitrary operand can be optimized within Optiland."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
